// import "./../../chartjs/Chart.plugins";
import { Component, OnInit, OnDestroy, ViewChild, AfterViewInit, ElementRef } from '@angular/core';
import { Subject } from "rxjs/Subject";
import { Observable } from "rxjs/Observable";
import { Subscription } from "rxjs/Subscription";
import { CmHttp } from "./../../http-module/http.service";
import { CHART_COLORS, CHART_LABELS, CHART_OPTIONS } from "./fund-flow-pie.chart";
import { Action } from "@ngrx/store";
import { SocketService } from "socket";
import { BASEURL, STOCKURL } from "shared";
import { KSOCKET_MAP } from "kchart";
import { compare } from "../../lib/tschart/src/lib/index"
import * as moment from "moment";
import * as OriginChart from "chart.js";

OriginChart.plugins.register({
  afterDatasetsDraw: function (chartInstance, easing) {
    // To only draw at the end of animation, check for easing === 1
    var ctx = chartInstance.chart.ctx;
    // console.debug("Chart Instance: ", chartInstance);
    if (!chartInstance.options.alwaysLable || chartInstance.config.type !== "pie") return;
    chartInstance.data.datasets.forEach(function (dataset, i) {
      var meta = chartInstance.getDatasetMeta(i);
      var validAccalc = dataset.data.reduce((acc, cur, index) => meta.data[index]["hidden"] ? acc : acc + cur, 0);
      if (!meta.hidden) {
        let numToHidden = 0;
        meta.data.forEach(function (element, index) {
          // Draw the text in black, with the specified font
          ctx.fillStyle = 'rgb(255, 255, 255)';
          // console.debug("Ele: ", element)
          if (element.hidden) return;
          var fontSize = 12;
          var fontStyle = 'normal';
          var fontFamily = 'Microsoft Yahei';
          ctx.font = OriginChart.helpers.fontString(fontSize, fontStyle, fontFamily);
          // Just naively convert to string for now
          var dataNumber = (dataset.data[index] / validAccalc * 100);
          if (dataNumber < 8) return;
          var dataString = dataNumber.toFixed(0).toString() + "%";

          // Make sure alignment settings are correct
          ctx.textAlign = 'center';
          ctx.textBaseline = 'middle';
          // var position = element.tooltipPosition();
          var position = getOuterAreaLabelPoint.call(element, fontSize);
          ctx.fillText(dataString, position.x, position.y);
        });
      }
    });
  }
});

function getOuterAreaLabelPoint(textOffset) {
  var vm = this._view;
  var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
    rangeFromCentre = vm.outerRadius / 5 * 3;
  // rangeFromCentre = vm.outerRadius - (textOffset || 20) + Math.abs(Math.sin(Math.PI / 2 + centreAngle)) * (textOffset || 20) * 0.8;
  return {
    x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
    y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
  };
};


const TOGGLE_DATA_STATUS = "TOGGLE_DATA_STATUS";
const RESET_DATA_STATUS = "TOGGLE_DATA_STATUS";
const UPDATE_ORIGIN_DATA = "UPDATE_ORIGIN_DATA";

const CATE_MAP = {
  sm: {
    color: "darker",
    label: "小单"
  },
  md: {
    color: "dark",
    label: "中单"
  },
  lg: {
    color: "normal",
    label: "大单"
  },
  xl: {
    color: "light",
    label: "超大单"
  }
};
const UPDATE_TIME_FORMAT = "MM/DD HH:mm";
const convertTime = (time?: string | Date) =>
  time
    ? moment(time).format(UPDATE_TIME_FORMAT)
    : moment().format(UPDATE_TIME_FORMAT);

/**
 * 画K线时候的一个数据节点
 * 
 * @export
 * @interface KData
 */
export interface KData {
  time: string | Date;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  balance: number;
  fundflow: number[];
}
const getFlowData =
  (ask, bid, chartIndexAsk, chartIndexBid, cate) =>
    Object.assign({
      ask: {
        amount: ask,
        chartIndex: chartIndexAsk,
        hidden: false,
      },
      bid: {
        amount: bid,
        chartIndex: chartIndexBid,
        hidden: true,
      },
      cate,
      diff: ask - bid
    });

const sanitizeKData =
  (kDataArray: KData[]) =>
    kDataArray.
      map(kdata => kdata.fundflow).
      reduce((preDay, nowDay) =>
        preDay.map((v, i, a) =>
          v + nowDay[i]
        )
      );

const getPuredData =
  arr =>
    arr.map((n, i, a) =>
      i % 2 !== 0 ? n - arr[i - 1] : null).
      filter(num =>
        num !== null);

const getRandomArray =
  length =>
    new Array(length).
      fill(2).
      map(i =>
        Math.floor(Math.random() * 3000));

@Component({
  // moduleId: module.id,
  selector: 'fund-flow-pie',
  templateUrl: 'fund-flow-pie.component.html',
  styleUrls: [
    'fund-flow-pie.component.less'
  ],
  inputs: [
    "stockSource",
    "isBg"
  ]
})
export class FundFlowPieComponent implements OnInit, OnDestroy, AfterViewInit {
  stockSource: Observable<any>;
  chartSubscription: Subscription;
  periodSource = new Subject();
  currentStock;
  originData;
  dataStatus;
  dataStatusSource = new Subject();
  dataStatusStore: Observable<any>;
  chart;
  @ViewChild("chart", { read: ElementRef })
  chartInstance: ElementRef;
  chartColors = CHART_COLORS;
  chartOptions = CHART_OPTIONS;
  chartLabels = CHART_LABELS;
  chartData: number[] = [];
  mergedData: number[];
  puredData: number[];
  sumedPuredData: number;
  updateTime: string;
  absSumPuredData: number;
  groupedData: { in: number[], out: number[] };
  cateMap = CATE_MAP;
  socketHandler;

  tabList = [
    {
      text: "今日",
      $period: "Day1",
      $lastNPoints: 1
    }, {
      text: "5日",
      $period: "Day7",
      $lastNPoints: 1
    }, {
      text: "10日",
      $period: "Day7",
      $lastNPoints: 2
    }
  ]

  constructor(private http: CmHttp, private socket: SocketService) {
    ////本元素待优化，代码丑的
    this.dataStatusStore = this.dataStatusSource.scan((state, action: Action) => {
      switch (action.type) {
        case RESET_DATA_STATUS:
          return Object;
        case UPDATE_ORIGIN_DATA:
          return action.payload.data.map(amount => { return { amount, hidden: false } });
        case TOGGLE_DATA_STATUS:
          return (state as any[]).map((item, i) => i === action.payload.index ? { amount: item.amount, hidden: !item.hidden } : item);
        default:
          return state;
      }
    }, []);
  }

  ngOnInit() {

  }

  ngAfterViewInit() {
    // console.debug("Chart Pie: AfterInit: ");
    let { http, stockSource, periodSource, chartInstance } = this;
    this.chart = new OriginChart(chartInstance.nativeElement, {
      data: {
        datasets: [
          {
            ...CHART_COLORS[0],
            labels: CHART_LABELS,
            borderWidth: [1, 1, 1, 1, 1, 1, 1,1],
            data: [],
          }
        ]
      },
      type: "pie",
      options: CHART_OPTIONS
    });
    let fullStock = this.stockSource.
      merge(periodSource).
      scan((preStockWithPeriod, currentOne, index) =>
        Object.assign({}, preStockWithPeriod, currentOne)).
      filter(stock => !!stock.symbol && stock.$period).publishReplay();
    fullStock.connect();
    this.chartSubscription = fullStock.subscribe(stock => {
      if (
        this.currentStock &&
        (this.currentStock.symbol !== stock["symbol"] || this.currentStock.$period !== stock["$period"])
      ) {
        this.socket.emit("unsub", { interval: KSOCKET_MAP[this.currentStock.$period], symbol: this.currentStock.symbol });
      }

      this.socket.emit("sub", { interval: KSOCKET_MAP[stock["$period"]], symbol: stock["symbol"] });
      this.currentStock = stock;
    });
    this.chartSubscription.add(
      fullStock.
        switchMap(stock =>
          http.get(`${STOCKURL}/api/v2/sc/charts/${stock["symbol"]}/kline?period=${stock["$period"]}&lastNPoints=${stock["$lastNPoints"]}`)).
        subscribe((kData: KData[]) => {
          kData = kData.data.sort(compare('time'))
          if (kData.length) {
            this.updateTime = convertTime(kData.slice(-1)[0].time);
          }
          // console.debug("FULL STOCK: ", kData);
          this.finalDataFactory(kData);
        })
    );
    this.chartSubscription.add(fullStock.subscribe(stock => console.debug("Chart Pie: Stock", stock)));
    //默认开启今日
    this.periodSource.next(this.tabList[0]);

    let that = this;
    function socketHandler(kData) {
      let k = Object.keys(kData).map(key => kData[key]).reduce(first => first);
      // console.debug("Chart Pie Socket: ", this.socket);
      if (
        k["symbol"] !== that.currentStock["symbol"] ||
        k["interval"] !== KSOCKET_MAP[that.currentStock["$period"]]
      ) return;
      that.updateTime = convertTime();
      that.finalDataFactory(that.originData, k);
    }
    this.socketHandler = socketHandler;
    this.socket.on("KChart", this.socketHandler);
  }

  ngOnDestroy() {
    if (this.chartSubscription) {
      this.chartSubscription.unsubscribe();
    }
    if (this.currentStock && this.currentStock.$period) {
      this.socket.emit("unsub", {
        interval: KSOCKET_MAP[this.currentStock.$period],
        symbol: this.currentStock.symbol
      });
    }
    this.socket.off("KChart", this.socketHandler);
  }

  public finalDataFactory(kDataArray, appendData?) {
    let originData = kDataArray;
    if (!originData) return;
    // console.debug("Chart Pie Factory: ", originData, appendData);
    if (appendData) {
      originData = originData.map(k => k["time"] === appendData["time"] ? appendData : k);
    }

    this.originData = originData;
    if (!kDataArray || !kDataArray.length) {
      return;
    }
    let fakeKData = sanitizeKData(kDataArray);
    let data = this.mergedData = [
      getFlowData(fakeKData[0], fakeKData[1], 4, 3, "sm"),
      getFlowData(fakeKData[2], fakeKData[3], 5, 2, "md"),
      getFlowData(fakeKData[4], fakeKData[5], 6, 1, "lg"),
      getFlowData(fakeKData[6], fakeKData[7], 7, 0, "xl"),
    ].reverse();

    this.chartData = data.map(d => [d.ask, d.bid]).reduce((acc, cur) => acc.concat(cur)).sort((pre, next) => pre.chartIndex - next.chartIndex).map(d => d.amount);
    this.puredData = data.map(d => d.diff);
    this.sumedPuredData = this.getSumOfArray(this.puredData);
    this.absSumPuredData = this.getAbsSumOfArray(this.puredData);
    this.chart.data.datasets[0].data = this.chartData;
    this.chart.update();
  }

  public setPeriod(period: { $period: string, $lastNPoint: number }) {
    // console.debug("Fund Flow Chart Set Period: ", period);
    this.periodSource.next(period);
  }

  public getSumOfArray(arr) {
    return arr.reduce((acc, cur) => acc + cur);
  }

  public getOneSumOfArray(arr, isOut = false) {
    if (isOut) {
      return arr.reduce((acc, cur, i) => i % 2 === 0 ? acc + cur : acc, 0);
    }
    return arr.reduce((acc, cur, i) => i % 2 !== 0 ? acc + cur : acc, 0);
  }

  public getAbsSumOfArray(arr) {
    return arr.reduce((acc, cur) => acc + Math.abs(cur), 0);
  }

  public getBorderWidth(num: number) {
    return (Math.abs(num) / this.absSumPuredData * 100).toFixed(0) + "px";
  }

  public getColorClass(cate, inOrOut) {
    return `flow-${inOrOut}-${CATE_MAP[cate]['color']}`;
  }

  public trackFlow(flow) {
    return flow.cate;
  }

  // events
  public chartClicked(e: any): void {
    // console.log(e);
  }

  public chartHovered(e: any): void {
    // console.log(e);
  }

}